package com.centerserver;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.DirectColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Arrays;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.LineBorder;

class H264Player extends JFrame {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private JPanel mViewer;
	private FileInputStream mFileInputStream;
	private int mWidth;
	private int mHeight;
	private int mTrans = 0x0f0f0f0f;
	private byte[] mPixel;
	private Graphics2D mGraphics2d;

	public native int InitDecoder(int width, int height);
	public native int UnInitDecoder();
	public native int DecodeNal(byte[] in, int insize, byte[] out);

	static {
		System.loadLibrary("H264Decoder");
	}

	public H264Player(String fileName, int width, int height) {

		super("H264Player");
//		fileName = "test.264";
//		width = 352;
//		height = 288;

		mWidth = width;
		mHeight = height;

		mPixel = new byte[mWidth * mHeight * 2];

		try {
			mFileInputStream = new FileInputStream(fileName);
		} catch (FileNotFoundException e) {
			System.out.println("FileNotFoundException in H264Player");
			e.printStackTrace();
		}

		mViewer = new JPanel();
		mViewer.setSize(mWidth, mHeight);
		mViewer.setBorder(new LineBorder(Color.blue, 2));

		add(mViewer);
		setBounds(0, 0, mWidth, mHeight);
		setUndecorated(false);

		setVisible(true);
		
		
		playVideo();
	}

	private void updateView() {

		// set the width & height of the BufferedImage
		int width = mWidth, height = mHeight;
		int size = width * height;

		// save the info of all the pixels
		int[] pixels = new int[size];
		Arrays.fill(pixels, 0);
		for (int i = 0; i < size; i++) {
			int b1 = mPixel[i * 2] < 0 ? 256 + mPixel[i * 2] : mPixel[i * 2];
			int b2 = mPixel[i * 2 + 1] < 0 ? 256 + mPixel[i * 2 + 1]
					: mPixel[i * 2 + 1];
			
			
			pixels[i] |= ((b2 & 0xF8) << 16);
			pixels[i] |= ((((b2 & 0x07) << 3) + (b1 >> 5)) << 10);
			pixels[i] |= ((b1 & 0x1F) <<3);
			
			//System.out.println(b1 + " " + b2 + " " + pixels[i]);
		}

		DataBuffer dataBuffer = new DataBufferInt(pixels, size);
		WritableRaster raster = Raster.createPackedRaster(dataBuffer, width,
				height, width, new int[] { 0xFF0000, 0xFF00, 0xFF }, null);
		DirectColorModel directColorModel = new DirectColorModel(24, 0xFF0000,
				0xFF00, 0xFF);
		BufferedImage image = new BufferedImage(directColorModel, raster, true,
				null);

		mGraphics2d = (Graphics2D) mViewer.getGraphics();
		mGraphics2d.drawImage(image, 0, 0, null);
	}

	int MergeBuffer(byte[] NalBuf, int NalBufUsed, byte[] SockBuf,
			int SockBufUsed, int SockRemain) {
		int i = 0;
		byte Temp;

		for (i = 0; i < SockRemain; i++) {
			Temp = SockBuf[i + SockBufUsed];
			NalBuf[i + NalBufUsed] = Temp;

			mTrans <<= 8;
			mTrans |= Temp;

			// find start code
			if (mTrans == 1) 
			{
				i++;
				break;
			}
		}

		return i;
	}

	public void playVideo() {

		int nalLen;

		boolean bFirst = true;
		boolean bFindPPS = true;

		int bytesRead = 0;
		int NalBufUsed = 0;
		int SockBufUsed = 0;

		byte[] NalBuf = new byte[307200]; // 300k
		byte[] SockBuf = new byte[2048];

		InitDecoder(mWidth, mHeight);

		while (!Thread.currentThread().isInterrupted()) {

			try {
				bytesRead = mFileInputStream.read(SockBuf, 0, 2048);
			} catch (IOException e) {
				System.out.println("IOException in H264Player");
			}

			if (bytesRead <= 0)
				break;

			SockBufUsed = 0;

			while (bytesRead - SockBufUsed > 0) {
				nalLen = MergeBuffer(NalBuf, NalBufUsed, SockBuf, SockBufUsed,
						bytesRead - SockBufUsed);

				NalBufUsed += nalLen;
				SockBufUsed += nalLen;

				while (mTrans == 1) {
					mTrans = 0xFFFFFFFF;

					if (bFirst == true) // the first start flag
					{
						bFirst = false;
					} else // a complete NAL data, include 0x00000001 trail.
					{
						if (bFindPPS == true) // true
						{
							if ((NalBuf[4] & 0x1F) == 7) {
								bFindPPS = false;
							} else {
								NalBuf[0] = 0;
								NalBuf[1] = 0;
								NalBuf[2] = 0;
								NalBuf[3] = 1;

								NalBufUsed = 4;

								break;
							}
						}
						// decode NAL
						int iTemp = DecodeNal(NalBuf, NalBufUsed - 4, mPixel);

						if (iTemp > 0)
						{
							updateView();
							try {
								Thread.sleep(1000/2);
							} catch (InterruptedException e) {
								System.out.println("InterruptedException in H264Player");
								e.printStackTrace();
							}
						}
					}

					NalBuf[0] = 0;
					NalBuf[1] = 0;
					NalBuf[2] = 0;
					NalBuf[3] = 1;

					NalBufUsed = 4;
				}
			}
		}

		try {
			if (mFileInputStream != null)
				mFileInputStream.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		UnInitDecoder();
	}
}
